package com.kly.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.kly.common.BaseResponse;
import com.kly.common.DeleteRequest;
import com.kly.common.ResultUtils;
import com.kly.constants.RedisConstant;
import com.kly.constants.UserConstant;
import com.kly.domain.dto.space.*;
import com.kly.domain.entity.Space;
import com.kly.domain.entity.User;
import com.kly.domain.enums.SpaceLevelEnum;
import com.kly.domain.vo.space.SpaceVO;
import com.kly.exception.BusinessException;
import com.kly.exception.ErrorCode;
import com.kly.exception.ThrowUtils;
import com.kly.manager.auth.SpaceUserAuthManage;
import com.kly.manager.auth.StpKit;
import com.kly.manager.auth.anno.SaSpaceCheckRole;
import com.kly.service.SpaceService;
import com.kly.service.UserService;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author admin
 * @date 2025/3/3
 */
@RestController
@RequestMapping("/space")
public class SpaceController {

    @Resource
    private SpaceService spaceService;

    @Resource
    private UserService userService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private SpaceUserAuthManage spaceUserAuthManage;


    private final Cache<String, String> LOCAL_CACHE = Caffeine.newBuilder()
            .initialCapacity(1024)
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .maximumSize(10_000)
            .build();


    /**
     * 删除空间
     */
    @PostMapping("/delete")
    public BaseResponse<Boolean> deleteSpace(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(deleteRequest == null || deleteRequest.getId() <= 0, ErrorCode.PARAMS_ERROR);

        //    校验权限
        User loginUser = userService.getLoginUser(request);
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NO_AUTH_ERROR, "没有权限");
        String userRole = loginUser.getUserRole();
        // 查询空间
        Long id = deleteRequest.getId();
        Space spaceDb = spaceService.getById(id);
        ThrowUtils.throwIf(spaceDb == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");
        if (!spaceDb.getUserId().equals(loginUser.getId()) && !UserConstant.ADMIN_ROLE.equals(userRole)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "没有权限");
        }

        // 删除空间cos TODO
        // spaceService.deleteSpaceCos(spaceDb);
        return ResultUtils.success(spaceService.removeById(deleteRequest.getId()));
    }

    /**
     * 更新空间 管理员
     */
    @PostMapping("/update")
    @SaSpaceCheckRole(UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> updateSpace(@RequestBody SpaceUpdateRequest spaceUpdateRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(spaceUpdateRequest == null || spaceUpdateRequest.getId() <= 0, ErrorCode.PARAMS_ERROR);
        Long id = spaceUpdateRequest.getId();
        //     查询空间
        Space spaceDb = spaceService.getById(id);
        ThrowUtils.throwIf(spaceDb == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");
        //     更新空间
        Space space = BeanUtil.copyProperties(spaceUpdateRequest, Space.class);
        spaceService.fillSpaceInfo(space);
        //     校验空间信息
        spaceService.validateSpaceInfo(space);
        // 删除空间cos TODO
        // spaceService.deleteSpaceCos(spaceDb);
        //     更新空间
        return ResultUtils.success(spaceService.updateById(space));
    }

    /**
     * 获取空间 管理员
     */
    @GetMapping("/get")
    @SaSpaceCheckRole(UserConstant.ADMIN_ROLE)
    public BaseResponse<Space> getSpaceById(Long id) {
        ThrowUtils.throwIf(id == null || id <= 0, ErrorCode.PARAMS_ERROR);
        Space space = spaceService.getById(id);
        ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");
        return ResultUtils.success(space);
    }

    /**
     * 获取空间 普通用户
     */
    @GetMapping("/get/vo")
    public BaseResponse<SpaceVO> getSpaceVOById(Long id, HttpServletRequest request) {
        ThrowUtils.throwIf(id == null || id <= 0, ErrorCode.PARAMS_ERROR);
        Space space = spaceService.getById(id);
        ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");
        SpaceVO spaceVO = spaceService.getSpaceVO(space, request);
        User loginUser = userService.getLoginUser(request);
        List<String> permissionList = spaceUserAuthManage.getPermissionList(space, loginUser);
        spaceVO.setPermissionList(permissionList);
        return ResultUtils.success(spaceVO);
    }


    /**
     * 分页获取空间列表 管理员
     */
    @GetMapping("/list/page")
    @SaSpaceCheckRole(UserConstant.ADMIN_ROLE)
    public BaseResponse<Page<Space>> listSpaceByPage(SpaceQueryRequest spaceQueryRequest) {
        ThrowUtils.throwIf(spaceQueryRequest == null, ErrorCode.PARAMS_ERROR);
        //     获取查询条件
        int current = spaceQueryRequest.getCurrent();
        int pageSize = spaceQueryRequest.getPageSize();
        QueryWrapper<Space> queryWrapper = spaceService.getQueryWrapper(spaceQueryRequest);
        Page<Space> page = spaceService.page(new Page<>(current, pageSize), queryWrapper);
        return ResultUtils.success(page);
    }

    /**
     * 分页获取空间列表 普通用户 通过缓存
     */
    @PostMapping("/list/page/vo/cache")
    public BaseResponse<Page<SpaceVO>> listSpaceVOByPageWithCache(@RequestBody SpaceQueryRequest spaceQueryRequest) {
        ThrowUtils.throwIf(spaceQueryRequest == null, ErrorCode.PARAMS_ERROR);
        int current = spaceQueryRequest.getCurrent();
        int pageSize = spaceQueryRequest.getPageSize();
        // 限制爬虫
        ThrowUtils.throwIf(pageSize > 20, ErrorCode.PARAMS_ERROR);
        // 查询缓存
        // 参数MD5
        String jsonStr = JSONUtil.toJsonStr(spaceQueryRequest);
        String token = DigestUtils.md5DigestAsHex(jsonStr.getBytes());

        // 本地缓存
        String key = String.format("listSpaceVoByPage:%s", token);
        String cacheCaffeine = LOCAL_CACHE.getIfPresent(key);
        // 设置过期时间 5-10分钟 防止缓存雪崩
        int expireTime = 5 + RandomUtil.randomInt(0, 5);
        if (StrUtil.isNotBlank(cacheCaffeine)) {
            Page<SpaceVO> page = JSONUtil.toBean(cacheCaffeine, Page.class);
            return ResultUtils.success(page);
        }

        if ("".equals(cacheCaffeine)) {
            //    防止缓存穿透
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "空间不存在");
        }

        // 设置key 通过项目名 + 方法名 + 参数（json）
        // redis缓存
        String cacheRedis = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(cacheRedis)) {
            Page<SpaceVO> page = JSONUtil.toBean(cacheRedis, Page.class);
            // 更新本地缓存
            LOCAL_CACHE.put(key, cacheRedis);
            return ResultUtils.success(page);
        }

        if ("".equals(cacheRedis)) {
            //    防止缓存穿透
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "空间不存在");
        }


        QueryWrapper<Space> queryWrapper = spaceService.getQueryWrapper(spaceQueryRequest);

        Page<Space> spacePage = spaceService.page(new Page<>(current, pageSize), queryWrapper);
        // 判断是否为空
        if (CollUtil.isEmpty(spacePage.getRecords())) {
            // redis
            stringRedisTemplate.opsForValue().set(key, "", expireTime, TimeUnit.MINUTES);
            // caffeine
            LOCAL_CACHE.put(key, "");
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "空间不存在");
        }

        Page<SpaceVO> spaceVOPage = spaceService.listSpaceVOByPage(spacePage);
        // 缓存到redis
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(spaceVOPage), expireTime, TimeUnit.MINUTES);
        // 缓存到caffeine
        LOCAL_CACHE.put(key, JSONUtil.toJsonStr(spaceVOPage));
        return ResultUtils.success(spaceVOPage);
    }

    /**
     * 分页获取空间列表 普通用户
     */
    @PostMapping("/list/page/vo")
    public BaseResponse<Page<SpaceVO>> listSpaceVOByPage(@RequestBody SpaceQueryRequest spaceQueryRequest) {
        ThrowUtils.throwIf(spaceQueryRequest == null, ErrorCode.PARAMS_ERROR);
        int current = spaceQueryRequest.getCurrent();
        int pageSize = spaceQueryRequest.getPageSize();
        // 限制爬虫
        ThrowUtils.throwIf(pageSize > 20, ErrorCode.PARAMS_ERROR);

        QueryWrapper<Space> queryWrapper = spaceService.getQueryWrapper(spaceQueryRequest);
        Page<Space> spacePage = spaceService.page(new Page<>(current, pageSize), queryWrapper);
        return ResultUtils.success(spaceService.listSpaceVOByPage(spacePage));
    }


    /**
     * 编辑空间 普通用户
     */
    @PostMapping("/edit")
    public BaseResponse<Boolean> editSpace(@RequestBody SpaceEditRequest spaceEditRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(spaceEditRequest == null || spaceEditRequest.getId() <= 0, ErrorCode.PARAMS_ERROR);
        // 1.获取空间信息
        Space spaceDb = spaceService.getById(spaceEditRequest.getId());
        ThrowUtils.throwIf(spaceDb == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");
        //     2. 校验空间信息
        Space space = BeanUtil.copyProperties(spaceEditRequest, Space.class);
        space.setEditTime(new Date());
        // 补充审核信息
        User loginUser = userService.getLoginUser(request);
        spaceService.fillSpaceInfo(space);
        spaceService.validateSpaceInfo(space);
        //     3. 校验用户权限
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NO_AUTH_ERROR, "没有权限");
        if (!spaceDb.getUserId().equals(loginUser.getId()) && !UserConstant.ADMIN_ROLE.equals(loginUser.getUserRole())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "没有权限");
        }
        // 删除空间cos TODO
        // spaceService.deleteSpaceCos(spaceDb);
        //     4. 更新空间
        return ResultUtils.success(spaceService.updateById(space));
    }

    /**
     * 添加空间
     */
    @PostMapping("/add")
    public BaseResponse<Long> addSpace(@RequestBody SpaceAddRequest spaceAddRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(spaceAddRequest == null, ErrorCode.PARAMS_ERROR);
        User loginUser = userService.getLoginUser(request);
        return ResultUtils.success(spaceService.addSpace(spaceAddRequest, loginUser));
    }


    /**
     * 返回空间等级数组
     */
    @GetMapping("/level/list")
    public BaseResponse<List<SpaceLevel>> listSpaceLevel() {
        List<SpaceLevel> spaceLevelList = Arrays.stream(SpaceLevelEnum.values())
                .map(spaceLevelEnum -> new SpaceLevel(
                        spaceLevelEnum.getName(),
                        spaceLevelEnum.getLevel(),
                        spaceLevelEnum.getMaxCount(),
                        spaceLevelEnum.getMaxSize()
                )).collect(Collectors.toList());
        return ResultUtils.success(spaceLevelList);
    }

    /**
     * 统计公有空间访问次数
     */
    @GetMapping("/visit/count")
    public BaseResponse<Long> countSpaceVisit() {
    //     1. 获取用户id
        Long userId = StpKit.SPACE.getLoginIdAsLong();
    //     设置key space + day
        String day = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        String key = String.format("%s:%s", RedisConstant.SPACE_VISIT_COUNT_KEY, day);
        //     3. 写入redis
        stringRedisTemplate.opsForHyperLogLog()
                .add(key, String.valueOf(userId));
        Long count = stringRedisTemplate.opsForHyperLogLog().size(key);
        return ResultUtils.success(count);
    }

}
